package cn.orz.study.config;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * @Author LHL
 * @Date 2020-11-08
 * @Description
 * @Version 1.0
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {


    @Bean
    @Primary
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder){
        //日期和时间格式化
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));

        return builder
                .modules(javaTimeModule)
                .simpleDateFormat("yyyy-MM-dd HH:mm:ss")
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                .defaultViewInclusion(false)
                .failOnUnknownProperties(false)
                .build();
    }

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper){
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper);
        mappingJackson2HttpMessageConverter.setDefaultCharset(UTF_8);
        return mappingJackson2HttpMessageConverter;
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        //configurer.ignoreAcceptHeader(true);
        //configurer.mediaType("json", MediaType.APPLICATION_JSON);
    }


    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new StringToEnumConverterFactory());
        //registry.addConverter(new StringToDateConverter());
    }


    static class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {

        @Override
        public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
            return new StringToEnum(getEnumType(targetType));
        }

        private Class<?> getEnumType(Class<?> targetType) {
            Class<?> enumType = targetType;
            while (enumType != null && !enumType.isEnum()) {
                enumType = enumType.getSuperclass();
            }
            if (enumType == null) {
                throw new IllegalArgumentException(
                        "The target type " + targetType.getName() + " does not refer to an enum");
            }
            return enumType;
        }


        private class StringToEnum<T extends Enum> implements Converter<String, T> {

            private final Class<T> enumType;

            public StringToEnum(Class<T> enumType) {
                this.enumType = enumType;
            }

            @Override
            public T convert(String source) {
                if (source.isEmpty()) {
                    return null;
                } else {
                    source = source.trim();
                    if(containsAnnotation(JsonCreator.class)){
                        return JSONUtil.toBean(source,enumType);
                    }
                    if (source.matches("\\d+")) {
                        int i = Integer.parseInt(source);
                        if (i >= 0 && i < this.enumType.getEnumConstants().length) {
                            return this.enumType.getEnumConstants()[i];
                        }
                    }
                    return (T) Enum.valueOf(this.enumType, source.trim());
                }
            }

            /**
             * 公共方法中是否包含指定注解类
             * @param annotationClass
             * @return
             */
            private boolean containsAnnotation(Class annotationClass){
                for(Method method : enumType.getMethods()){
                    if(method.getDeclaredAnnotation(annotationClass)!=null){
                        return true;
                    }
                }
                return false;
            }
        }
    }
/*

    private class StringToDateConverter implements Converter<String,Date>{


        @Override
        public Date convert(String source) {
            if(source.matches("^[\\-0-9]+$")){
                return new Date(Long.parseLong(source));
            }
            return null;
        }
    }*/

}
